﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SHARED;
using System.Data.SqlClient;
using System.Xml.Linq;

namespace IPL
{
    public class Parser
    {
        #region Properties

        private Lexer _lex { get; set; }
        private ParserOutput _request { get; set; }
        private int _currentLevel = 0;
        private Stack<MasterElement> _masterElementStack = new Stack<MasterElement>();
        private Stack<MasterElement> _masterElementStackReverse = new Stack<MasterElement>();

        #endregion

        #region Constructors

        public Parser(Lexer lex)
        {
            _lex = lex;
            _request = new ParserOutput();
        }

        #endregion

        #region Public Methods

        public ParserOutput Parse()
        {
            _masterElementStack.Push(_request.RootElement = new MasterElement(_currentLevel++));
            _masterElementStack.Peek().elements.Add(new Element("Results", true));
            _request.ElementList = new MasterElementList(_request.RootElement);

            //TESTING();
            START();
#if false
            throw new Exception("Parse Success");
#endif
            END();
            while (_masterElementStackReverse.Count > 0)
                _request.ElementList.Add(_masterElementStackReverse.Pop());

            return _request;
        }

        #endregion

        #region Private Helper Methods

        private Token accept(params string[] args)
        {
            if (args[0] != "ANY")
                expecting(args);
            _lex.Accept();
            return _lex.LookBehind(1);
        }
        private Token accept(params TokenType[] args)
        {
            expecting(args);
            _lex.Accept();
            return _lex.LookBehind(1);
        }

        private void expecting(params string[] args)
        {
            KeyValuePair<bool, string> expected = isA(args);
            if (!expected.Key) error(expected.Value);
        }
        private void expecting(params TokenType[] args)
        {
            KeyValuePair<bool, string> expected = isA(args);
            if (!expected.Key) error(expected.Value);
        }

        private KeyValuePair<bool, string> isA(params string[] args)
        {
            bool isExpected = false;
            string expected = "";
            foreach (string expecting in args)
            {
                if (_lex.Current().Value.Equals(expecting, StringComparison.CurrentCultureIgnoreCase)) isExpected = true;
                expected += expecting + " | ";
            }
            return new KeyValuePair<bool, string>(isExpected, expected.Trim(' ', '|'));
        }
        private KeyValuePair<bool, string> isA(params TokenType[] args)
        {
            bool isExpected = false;
            string expected = "";
            foreach (TokenType expecting in args)
            {
                if (_lex.Current().TokenType == expecting) isExpected = true;
                expected += expecting.ToString() + " | ";
            }
            return new KeyValuePair<bool, string>(isExpected, expected.Trim(' ', '|'));
        }

        private void error(string expecting)
        {
            throw new Exception(String.Format("Expecting \"{0}\" on line {1} not \"{2}\"", expecting, _lex.Current().LineNum, _lex.Current().Value.ToString()));
        }

        #endregion

        #region Private Grammar Rule Methods

        private void START()
        {
            SELECT_CLAUSE();
            _currentLevel--;
            _masterElementStackReverse.Push(_masterElementStack.Pop());
            _request.SQL += accept("from").Lexem + " ";
            while (!_lex.End())
            {
                _request.SQL += accept("ANY").Lexem + " ";
            }

        }
        private void END()
        {
            if (!_lex.End()) error("EOF");
        }

        private void SELECT_CLAUSE()
        {
            _request.SQL += accept("select").Lexem + " ";
            COLUMN_LIST();
        }
        private void COLUMN_LIST()
        {
            String column = "";
            if (isA("<", "+", "*", "?").Key)
            {
                if (isA("<").Key)
                {
                    accept("<");
                    //TODO set MasterElement type to grouping
                }
                if (isA("+", "*", "?").Key)
                {
                    accept("+", "*", "?");
                    //TODO set MasterElement type to compression
                }
                if (!isA(",").Key)
                {
                    column = accept(TokenType.Identifier).Lexem;
                }
                MasterElement newMasterElement = new MasterElement(_currentLevel++);
                newMasterElement.Parent = _masterElementStack.Peek();
                _masterElementStack.Push(newMasterElement);
                _masterElementStack.Peek().AddElement(new Element(column, true));
                accept(",");
            }
            
            {
                column = accept(TokenType.Identifier).Lexem;
                _request.SQL += column + " ";

                if (isA("as").Key) // handle alias
                {
                    _request.SQL += accept("as").Lexem + " ";
                    column = accept(TokenType.Identifier).Lexem;
                    _request.SQL += column + " ";
                    
                }
                _masterElementStack.Peek().AddElement(new Element(column.Split('.').Last()));
            }

            while (isA(">").Key)
            {
                accept(">");
                _currentLevel--;
                _masterElementStackReverse.Push(_masterElementStack.Pop());
            }

            if (isA(",").Key) // more columns in select... uno mas!
            {
                _request.SQL += accept(",").Lexem + " ";
                COLUMN_LIST();
            }
        }

        public void TESTING()
        {
            //master element -> level 1
            Element SysStem = new Element("DepartmentName", true);
            Element SystemInfo = new Element("Name");
            MasterElement Systems = new MasterElement(1);
            Systems.AddElement(SysStem);
            Systems.AddElement(SystemInfo);
            Systems.Parent = _request.RootElement;

            //master element -> level 2
            Element systemID = new Element("Physicianinfo", true);
            Element AppID = new Element("FirstName");
            Element ZipCode = new Element("LastName");
            MasterElement sysDetails = new MasterElement(2);
            sysDetails.AddElement(systemID);
            sysDetails.AddElement(AppID);
            sysDetails.AddElement(ZipCode);
            sysDetails.Parent = Systems;

            MasterElementList list = new MasterElementList(_request.RootElement);
            list.Add(Systems);
            list.Add(sysDetails);

            _request.ElementList = list;
            _request.SQL = "Select Department.Name, Physician.FirstName, Physician.LastName FROM Department, Physician Where Department.DeptNo = Physician.DeptNo";
        }

        #endregion

    }

    public class ParserOutput
    {
        public String SQL = "";
        public MasterElement RootElement;
        public MasterElementList ElementList;
    }

    public class ParentElementStack
    {
        private IList<MasterElement> _stack = new List<MasterElement>();
        public ParentElementStack() { }

        public void push(MasterElement element)
        {
            //Stack
        }

    }


}
